#include <stdlib.h>
#include <stdio.h>
#ifdef WIN32
#include <conio.h>
#include <direct.h>
#include <io.h>
#define FILE_SEPARATOR '\\'
#else
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#define FILE_SEPARATOR '/'
#endif
#include <string.h>
#include <math.h>
#include <tiffio.h>
#include <xtiffio.h>
#include <geo_tiffp.h>
#include <geotiff.h>
#include <geo_normalize.h>
#include <geovalues.h>

#include <vector>
#include <iostream>
// #include <filesystem>

int TIFImageToPCS(TIFF *tif, double *x, double *y)
{
    int res = 0;
    short tiepoint_count, count, transform_count; // 这个几个变量必须为short类型，否则读取数据错位了。
    double *tiepoints = 0;
    double *pixel_scale = 0;
    double *transform = 0;

    if (!TIFFGetField(tif, TIFFTAG_GEOTIEPOINTS,
                      &tiepoint_count, &tiepoints))
    {
        tiepoint_count = 0;
    }

    if (!TIFFGetField(tif, TIFFTAG_GEOPIXELSCALE, &count, &pixel_scale))
    {
        count = 0;
    }

    if (!TIFFGetField(tif, TIFFTAG_GEOTRANSMATRIX,
                      &transform_count, &transform))
    {
        transform_count = 0;
    }

    /* -------------------------------------------------------------------- */
    /*      If the pixelscale count is zero, but we have tiepoints use      */
    /*      the tiepoint based approach.                                    */
    /* -------------------------------------------------------------------- */
    if (tiepoint_count > 6 && count == 0)
    {
    }

    /* -------------------------------------------------------------------- */
    /*	If we have a transformation matrix, use it. 			*/
    /* -------------------------------------------------------------------- */
    else if (transform_count == 16)
    {
        double x_in = *x, y_in = *y;

        *x = x_in * transform[0] + y_in * transform[1] + transform[3];
        *y = x_in * transform[4] + y_in * transform[5] + transform[7];

        res = 1;
    }

    /* -------------------------------------------------------------------- */
    /*      For now we require one tie point, and a valid pixel scale.      */
    /* -------------------------------------------------------------------- */
    else if (count < 3 || tiepoint_count < 6)
    {
        res = 0;
    }

    else
    {
        *x = (*x - tiepoints[0]) * pixel_scale[0] + tiepoints[3];
        *y = (*y - tiepoints[1]) * (-1 * pixel_scale[1]) + tiepoints[4];

        res = 1;
    }

    return res;
}

void SetUpGeoKeys(TIFF *tif)
{
    GTIF *gtif = GTIFNew(tif);
    if (gtif == NULL)
    {
        return;
    }
    GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1, ModelGeographic);
    GTIFKeySet(gtif, GTRasterTypeGeoKey, TYPE_SHORT, 1, RasterPixelIsPoint);
    GTIFKeySet(gtif, GeographicTypeGeoKey, TYPE_SHORT, 1, GCS_WGS_84);
    GTIFKeySet(gtif, GeogAngularUnitsGeoKey, TYPE_SHORT, 1, Angular_Degree);
    // GTIFKeySet(gtif, GeogEllipsoidGeoKey, TYPE_SHORT,     1, Ellipse_Everest_1830_1967_Definition);
    GTIFKeySet(gtif, GeogEllipsoidGeoKey, TYPE_SHORT, 1, Ellipse_WGS_84);
    // GTIFKeySet(gtif, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1, (double)6378137.0000000000);
    // GTIFKeySet(gtif, GeogSemiMinorAxisGeoKey, TYPE_DOUBLE, 1, (double)6356752.3142449996);
    // GTIFKeySet(gtif, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1, (double)298.25722356049300);
    GTIFKeySet(gtif, VerticalUnitsGeoKey, TYPE_SHORT, 1, Linear_Meter);
    // GTIFKeySet(gtif, GTCitationGeoKey, TYPE_ASCII, 0, "Just An Example");
    // GTIFKeySet(gtif, GeogCitationGeoKey, TYPE_ASCII, 0, "WGS 84");
    // GTIFKeySet(gtif, GeogLinearUnitsGeoKey, TYPE_SHORT,  1, Linear_Meter);
    // GTIFKeySet(gtif, GeogGeodeticDatumGeoKey, TYPE_SHORT,     1, KvUserDefined);
    GTIFWriteKeys(gtif);
    GTIFFree(gtif);
}

TIFF *CreateGeoTiff(const char *lpszFile, TIFF *tif)
{
    if (tif == NULL)
    {
        return NULL;
    }

    TIFF *tf2 = TIFFOpen(lpszFile, "w");
    if (tf2 == NULL)
    {
        return NULL;
    }

    short count;
    tsample_t nT;
    int width, height;
    double *dTiepoints = 0;
    double *dPixscale = 0;

    TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
    TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
    TIFFSetField(tf2, TIFFTAG_IMAGEWIDTH, width);
    TIFFSetField(tf2, TIFFTAG_IMAGELENGTH, height);

    TIFFGetField(tif, TIFFTAG_GEOTIEPOINTS, &count, &dTiepoints);
    TIFFGetField(tif, TIFFTAG_GEOPIXELSCALE, &count, &dPixscale);
    TIFFSetField(tf2, TIFFTAG_GEOTIEPOINTS, 6, dTiepoints);
    TIFFSetField(tf2, TIFFTAG_GEOPIXELSCALE, 3, dPixscale);

    TIFFSetField(tf2, TIFFTAG_BITSPERSAMPLE, 8);
    TIFFSetField(tf2, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
    TIFFSetField(tf2, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    TIFFSetField(tf2, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    // TIFFSetField(tf2, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT);  // 该语句特别重要，否则将无法识别无高程数据点的数值（-9999）
    TIFFSetField(tf2, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);

    nT = 3;
    TIFFSetField(tf2, TIFFTAG_SAMPLESPERPIXEL, nT);

    TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &width);
    TIFFSetField(tf2, TIFFTAG_ROWSPERSTRIP, width);

    return tf2;
}

bool Dem2RGB(const char *lpszIn, const char *lpszOut)
{
    // if (access(lpszIn, 0) == 0)
    //{
    //	return false;
    // }

    short nNoData;
    int type;
    int width, height;
    TIFF *tif;
    TIFF *tf2;
    double ulx, uly, llx, lly, lrx, lry;

    XTIFFInitialize();
    tif = TIFFOpen(lpszIn, "r");
    if (tif != NULL)
    {
        TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
        TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);

        //ulx = 0.0;
        //uly = 0.0;
        //llx = 0.0;
        //lly = (double)height;
        //lrx = (double)width;
        //lry = (double)height;

        //TIFImageToPCS(tif, &ulx, &uly);
        //TIFImageToPCS(tif, &llx, &lly);
        //TIFImageToPCS(tif, &lrx, &lry);

        tf2 = CreateGeoTiff(lpszOut, tif);

        type = TIFFIsTiled(tif);
        std::vector<char> vs(width * 3);
        char *pT = vs.data();
        short *pS = (short *)pT;
        if (type == 0) // 条带化 strips
        {
            tstrip_t strip;
            tsize_t scanline;
            int nrow;
            int rowsperstrip = -1;
            tsample_t samplesperpixel;

            // tstrip_t nstrips = TIFFNumberOfStrips(tif);
            scanline = TIFFScanlineSize(tif);

            TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
            TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);

            char *pTmp = NULL;
            if (TIFFGetField(tif, TIFFTAG_GDAL_NODATA, &pTmp))
            {
                // printf("%s\n", pTmp);
                nNoData = atoi(pTmp);
            }
            else
            {
                nNoData = -32768;
            }

            for (int i = 0; i < height; i++)
            {
                nrow = (i + rowsperstrip > height ? height - i : rowsperstrip);
                strip = TIFFComputeStrip(tif, i, 0);
                TIFFReadEncodedStrip(tif, strip, pT, nrow * scanline);
                for (int j = width - 1; j >= 0; j--)
                {
                    unsigned char r = 0;
                    unsigned char g = 0;
                    unsigned char b = 0;
                    if (nNoData != pS[j])
                    {
                        int nT = (pS[j] * 10 + 100000) & 0x00FFFFFF;
                        r = nT >> 16;
                        g = (nT >> 8) & 0xFF;
                        b = nT & 0xFF;
                    }
                    char *p = pT + j * 3;
                    *p = r;
                    p++;
                    *p = g;
                    p++;
                    *p = b;
                }
                TIFFWriteEncodedStrip(tf2, strip, pT, width * 3);
            }
        }
        TIFFClose(tif);

        SetUpGeoKeys(tf2);
        TIFFClose(tf2);
    }
    return true;
}

void mkdirs(const char *muldir)
{
    int i, len;
    char str[512];
    strncpy(str, muldir, 512);
    len = strlen(str);
    for (i = 0; i < len; i++)
    {
        if (str[i] == FILE_SEPARATOR)
        {
            str[i] = '\0';
            if (access(str, 0) != 0)
            {
#ifdef WIN32
                mkdir(str);
#else
                mkdir(str, 0777);
#endif // WIN32
            }
            str[i] = FILE_SEPARATOR;
        }
    }
    // if (len > 0 && access(str, 0) != 0)
    //{
    //     mkdir(str, 0777);
    // }
    return;
}

// namespace fs = std::filesystem;
// void Traversal(const char *lpszPath)
//{
//     std::string rgb = "../rgb/";
//     // mkdir(rgb.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
//     const auto &entrys = fs::recursive_directory_iterator(lpszPath);
//     for (const auto &entry : entrys)
//     {
//         if (entry.is_regular_file())
//         {
//             const auto &path = entry.path();
//             const auto &ext = path.extension();
//             if (ext == ".tif")
//             {
//                 // const auto &filename = path.filename();
//                 // std::cout << "File: " << path << std::endl;
//                 const std::string &pth = path.string();
//                 // 此处必须分成两行，定义临时变量，如果直接写成const char *pDst = (rgb + pth).c_str();样式，pDst中的值不正确
//                 const std::string &dst = rgb + pth;
//                 const char *pDst = dst.c_str();
//                 // const char *pDst = (rgb + pth).c_str();
//                 mkdirs(pDst);
//                 Dem2RGB(pth.c_str(), pDst);
//             }
//         }
//     }
// }

#define MAX_FILENAME_LENGTH 1024
#ifdef WIN32

int IsDir(char *lpszFilePath)
{
    struct stat st;

    if (stat(lpszFilePath, &st) == -1)
    {
        return 0;
    }

    return ((st.st_mode & S_IFMT) == S_IFDIR) ? 1 : 0;
}

int IsFile(char *lpszFilePath)
{
    struct stat st;

    if (stat(lpszFilePath, &st) == -1)
    {
        return 0;
    }

    return ((st.st_mode & S_IFMT) == S_IFREG) ? 1 : 0;
}

void Traversal(char *lpszDir)
{
    long long hFile;
    struct _finddata_t finddata;
    char szBuf[MAX_FILENAME_LENGTH];

    sprintf(szBuf, "%s\\*.*", lpszDir);
    hFile = _findfirst(szBuf, &finddata);
    while (hFile >= 0)
    {
        if (strcmp(finddata.name, ".") == 0 || strcmp(finddata.name, "..") == 0)
        {
        }
        else
        {
            sprintf(szBuf, "%s\\%s", lpszDir, finddata.name);
            if (IsFile(szBuf))
            {
                int len = strlen(finddata.name);
                // if (len < 5)
                //     continue;
                // if (stricmp(finddata.name + len - 4, ".tif") == 0)
                // 上述写法在Linux系统没有任何逻辑错误，此后没有需要执行的语句了，在Windows就不行了，此后还需要执行_findnext
                if (len >= 5 && stricmp(finddata.name + len - 4, ".tif") == 0)
                {
                    sprintf(szBuf, "..\\rgb\\%s\\%s", lpszDir, finddata.name);
                    mkdirs(szBuf);
                    Dem2RGB(szBuf + 7, szBuf);
                }
            }
            else if (IsDir(szBuf))
            {
                Traversal(szBuf);
            }
        }

        if (_findnext(hFile, &finddata) < 0)
        {
            break;
        }
    }
    _findclose(hFile);
}

#else

int Traversal(const char *dirpath)
{
    // Bad address
    if (!dirpath || !strlen(dirpath))
    {
        return EFAULT;
    }

    //  获得目录结构体指针
    DIR *pDir = opendir(dirpath);
    if (pDir == NULL)
    {
        return EFAULT;
    }

    // 遍历该目录下文件，遇到目录则递归遍历
    int err = 0;
    struct dirent *ent;
    char szPath[MAX_FILENAME_LENGTH];
    while (ent = readdir(pDir))
    {
        // 目录，递归
        if (ent->d_type == DT_DIR)
        {
            if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
            {
                continue;
            }
            // 判断目标路径是否过长
            if (strlen(dirpath) + ent->d_reclen + 1 >= MAX_FILENAME_LENGTH)
            {
                return ENAMETOOLONG;
            }
            // 拼接出目标目录的路径
            sprintf(szPath, "%s/%s", dirpath, ent->d_name);
            // printf("szPath: %s\n", szPath);

            // 递归访问该目录
            err = Traversal(szPath);
            memset(szPath, 0, MAX_FILENAME_LENGTH);
            if (err != 0)
                break;
        }
        // 普通文件
        else if (ent->d_type == DT_REG)
        {
            // TODO
            if (err != 0)
                break;
            int len = strlen(ent->d_name);
            // if (len < 5)
            //     continue;
            // if (strcasecmp(ent->d_name + len - 4, ".tif") == 0)
            // 上述写法在Linux系统没有任何逻辑错误，此后没有需要执行的语句了，在Windows就不行了，此后还需要执行_findnext
            if (len >= 5 && strcasecmp(ent->d_name + len - 4, ".tif") == 0)
            {
                sprintf(szPath, "../rgb/%s/%s", dirpath, ent->d_name);
                mkdirs(szPath);
                Dem2RGB(szPath + 7, szPath);
            }
        }
    }

    closedir(pDir);
    return err;
}
#endif

int main(int argc, char **argv)
{
    // Traversal(".");
    // Dem2RGB("N20E125.tif", "N20E125rgb.tif");
#ifdef WIN32
    mkdir("../rgb/");
#else
    mkdir("../rgb/", 0777);
#endif // WIN32
    Traversal(".");
    return 0;
}
